#pragma once

#include "Types.h"
#include "DLL.h"
#include "Action.h"

#include <MyFC/Event.h>
#include <set>
#include <boost/shared_ptr.hpp>

namespace My {


class Selection;
typedef boost::shared_ptr<Selection> SelectionSPtr;
	
class DLL_API Doc : 
	public virtual EventDispatcher,
	public EventListener
{
	friend class ActionQueue;
	friend class Selection;
	friend class SelectionUpdate;
public:
	Doc();
	virtual ~Doc();
public:
	virtual void Init();
	virtual bool IsEmpty() const;
	virtual String GetFileName() const;
	virtual bool IsModified() const;
	virtual void New();
	virtual void Open(const String &, int nFilter = -1);
	virtual bool CanClose();
	virtual void Close();
	virtual void Save();
	virtual void SaveAs(const String &);
public:
	virtual void ProcessEvent(Event &);
// Action support:
public: 
	const ActionQueue & GetActions() const;
	ActionQueue & GetActions();
	bool Perform(Action * pAction);
// Selection support
public:
	Selection & GetSelection() const;
	void SetSelection(Selection * ptr);
private:
	void BeginSelectionUpdate();
	void EndSelectionUpdate();
private:
	ActionQueue		m_Actions;
	String			m_FileName;
	SelectionSPtr	m_pSelection;
	Size			m_SelectionUpdateCounter;
protected:
	virtual void SetFileName(String);
	virtual void SaveTo(const String &);
	virtual void DoOpen(const String &, int) { }
	virtual void DoSave(const String &) { }
	/// By default, dispatches a SelectionChange event
	virtual void SelectionChanged();
// Events:
public:
	class Event : public My::Event {
	public:
		Doc &	m_Doc;
		Event(Doc & d) : m_Doc(d) { }
	};
	class ActionsChange : public Event {
	public:
		ActionsChange(Doc & d) : Event(d) { }
	};
	class FileNameChange : public Event {
	public:
		FileNameChange(Doc & d) : Event(d) { }
	};
	class SelectionChange : public Event {
	public:
		SelectionChange(Doc & d) : Event(d) { }
	};
};

/**
 * Helper class used as selection update brackets
 */
class DLL_API SelectionUpdate {
public:
	SelectionUpdate();
	~SelectionUpdate();
};

class DLL_API Selection : public EventListener
{
	friend class Doc;
public:
	Selection();
	virtual ~Selection();
public:
	template <class T> bool Is() const
		{ return dynamic_cast<const T *>(this) != 0; }
	template <class T> const T & As() const
		{ return * dynamic_cast<const T *>(this); }
	template <class T> T & As()
		{ return * dynamic_cast<T *>(this); }
	Doc & GetDoc() const
		{ assert(m_pDoc != 0); return * m_pDoc; }
public:
	virtual bool IsEmpty() const = 0;
	/// Deselects all objects
	virtual void Clear() = 0;
	/// Can erase selected objects?
	virtual bool Erasable() const = 0;
	/// Erases selected objects
	virtual void Erase() = 0;
	virtual bool Equals(const Selection &) const = 0;
	virtual void BeginUpdate() = 0;
	virtual void EndUpdate() = 0;
public:
	virtual void ProcessEvent(Event &) { }
protected:
	/// Call this when something has changed
	void Changed();
private:
	Doc * m_pDoc;
};

/**
CmdUI used to dispatch command updates in a group of objects.
Supports enabled/disabled state only.
*/
class GroupCmdUI : public CCmdUI {
public:
	bool	m_Enabled;
public:
	GroupCmdUI() : m_Enabled(false) { }
public:
	virtual void Enable(BOOL bOn = TRUE) 
	{
		m_Enabled |= (bOn == TRUE);
		m_bEnableChanged = true;
	}
};

/**
 * A selection which is always empty
 */
class DLL_API NullSelection : public Selection {
public:
	virtual bool IsEmpty() const { return true; }
	virtual void Clear() { }
	virtual bool Erasable() const { return false; }
	virtual void Erase() { }
	virtual bool Equals(const Selection & s) const
		{ return s.Is<NullSelection>(); }
	virtual void BeginUpdate() { }
	virtual void EndUpdate() { }
};

/**
A group of objects of type T;
T must be descendant of CompItem.
 */
template <typename T>
class TypedSelection : public Selection {
public:
	typedef std::set<T *> ObjectSet;
public:
	TypedSelection();
	~TypedSelection();
public:
	const ObjectSet & GetObjects() const
		{ return m_Objects; }
	bool IsSelected(const T &) const;
	void Select(T &);
	void Deselect(T &);
public:
	virtual bool IsEmpty() const;
	virtual void Clear();
	virtual bool Erasable() const 
		{ return false; }
	virtual void Erase() 
		{ }
	virtual bool Equals(const Selection & s) const 
		{ return false; }
	virtual void BeginUpdate();
	virtual void EndUpdate();
public:
	virtual void ProcessEvent(Event &);
private:
	ObjectSet	m_Objects;
	bool		m_Update;
	bool		m_Changed;
};

template <typename T>
bool IsSelected(const T &);

template <typename T>
void Select(T &, bool);

Selection & GetSelection();
void SetSelection(Selection * ptr);
bool Perform(Action *);

} // My

#include "Impl/Doc.inl"